import * as THREE from 'three';
import * as fflate from './libs/fflate.module.js';
import { XMLParser } from 'fast-xml-parser';

/**
 * plateInfo: {
 *   plater_id,
 *   plater_name
 *   modelInfos: [
 *     {
 *       unit, id, position, vertexIndices, paintColors, // 获取来源: 3D/Objects/*.model
 *       object_id, extruder, name, modelSettingsTransform, // 获取来源: Metadata/model_settings.config
 *       modelTransform, // 获取来源: 3D/3dmodel.model
 *       geometryGroups // 获取来源: paintColors + extruder 计算得到；格式: {start, count, materialIndex}
 *     },
 *     ...
 *   ]
 * }
 */
const COLOR_INDEX = [
    '', // 第一个颜色
    '4', // 还是第一个颜色
    '8', // 第二个颜色
    '0C',
    '1C',
    '2C',
    '3C',
    '4C',
    '5C',
    '6C',
    '7C',
    '8C',
    '9C',
    'AC',
    'BC',
    'CC',
    'DC'
];

function generateGeometryGroups(paintColors, extruder) {
    const groups = [];
    if (!paintColors.length) return groups;

    let currentPaintColor = paintColors[0];
    let startFace = 0;

    for (let i = 1; i < paintColors.length; i++) {
        if (paintColors[i] !== currentPaintColor) {
            // 一个 group 结束
            const filamentIndex = COLOR_INDEX.includes(currentPaintColor)
                ? COLOR_INDEX.indexOf(currentPaintColor)
                : extruder;
            groups.push({
                start: startFace * 3, // 每个面有 3 个索引
                count: (i - startFace) * 3, // faces * 3 indices
                materialIndex: filamentIndex - 1
            });
            // 新 group 开始
            startFace = i;
            currentPaintColor = paintColors[i];
        }
    }

    // 处理最后一个 group
    const filamentIndex = COLOR_INDEX.includes(currentPaintColor)
        ? COLOR_INDEX.indexOf(currentPaintColor)
        : extruder;
    groups.push({
        start: startFace * 3,
        count: (paintColors.length - startFace) * 3,
        materialIndex: filamentIndex - 1
    });

    return groups;
}

/**
 * 将 3MF 文件中的 transform 字符串转换为 THREE.Matrix4
 * 例如： "1 0 0 0 1 0 0 0 1 175 175 10"
 * 
 在 3MF 中，transform 属性是一个由 12 个浮点数组成的列表：
 transform="a b c d e f g h i j k l"
表示一个 4×4 矩阵 的前 3 行（最后一行固定为 0 0 0 1）
| a  b  c  j |
| d  e  f  k |
| g  h  i  l |
| 0  0  0  1 |
 */
function transformToMatrix4(transformStr) {
    console.log(transformStr);
    if (!transformStr) return new THREE.Matrix4(); // 默认返回单位矩阵

    // 拆分为数字数组
    const nums = transformStr.trim().split(/\s+/).map(parseFloat);

    if (nums.length !== 12) {
        console.warn('Invalid transform string:', transformStr);
        return new THREE.Matrix4();
    }

    // 对应关系：
    // | a b c j |
    // | d e f k |
    // | g h i l |
    // | 0 0 0 1 |
    const [a, b, c, d, e, f, g, h, i, j, k, l] = nums;

    const matrix = new THREE.Matrix4();
    matrix.set(a, b, c, j, d, e, f, k, g, h, i, l, 0, 0, 0, 1);

    // 💡 修正旋转方向
    // const fix = new THREE.Matrix4().makeScale(1, -1, 1);
    // matrix.multiply(fix);

    return matrix;
}

const eIncompatible = new Error('Incompatible with this file');
const eMissingData = new Error('Missing necessary data');

class index extends THREE.Loader {
    constructor(manager) {
        super(manager);
    }

    load(url, onLoad, onProgress, onError) {
        const scope = this;
        const loader = new THREE.FileLoader(scope.manager);
        loader.setPath(scope.path);
        loader.setResponseType('arraybuffer');
        loader.setRequestHeader(scope.requestHeader);
        loader.setWithCredentials(scope.withCredentials);
        loader.load(
            url,
            function (buffer) {
                // 工具
                const textDecoder = new TextDecoder();
                const xmlParser = new XMLParser({
                    ignoreAttributes: false, // 关键配置：保留所有属性
                    attributeNamePrefix: '@_', // 关键配置：属性前缀
                    textNodeName: '#text', // 关键配置：文本内容的键名
                    parseTagValue: true, // 尝试将字符串转为数字、布尔值等
                    parseAttributeValue: true, // 尝试将字符串转为数字、布尔值等
                    trimValues: true, // 去除值的首尾空格
                    parseTrueNumberOnly: false, // 宽松的数字解析策略；“45.67” 也会解析为数字
                    arrayMode: true, // 强制数组 - 所有元素都放在数组中
                    // Relationships.Relationship: 3D/_rels/3dmodel.model.rels
                    // Metadata/model_settings.config: config.object, config.object.metadata, config.plate, config.assemble.assemble_item, config.plate.model_instance
                    isArray: (tagName, jPath, isLeafNode, isAttribute) => {
                        return [
                            'Relationships.Relationship',
                            'config.object',
                            'config.object.metadata',
                            'config.plate',
                            'config.assemble.assemble_item',
                            'config.plate.model_instance',
                            'model.build.item'
                        ].includes(jPath);
                    }
                });

                try {
                    // 提取的数据
                    let filament_colour = null;
                    let printer_model = null;
                    const plateInfos = [];

                    const modelInfosAll = []; // 所有的 modelInfo

                    // zip: {k, v}, k 是文件路径，v 是 Uint8Array
                    // 例如：{"3D/3dmodel.model" : Uint8Array, ...}
                    const zip = fflate.unzipSync(new Uint8Array(buffer));

                    {
                        // 1. 解析 Metadata/creality.config，并检查是否兼容此 3mf 文件
                        // 文件必须存在 && Company === "Creality" && Application === "Creality_Print" 或 "MakeNow"
                        const path = 'Metadata/creality.config';
                        const file = zip[path];

                        if (!file) {
                            onError(eIncompatible);
                            return;
                        }

                        const xmlStr = textDecoder.decode(file);
                        const obj = xmlParser.parse(xmlStr);
                        const metadatas = obj.config?.metadata || [];
                        const metadataCompany = metadatas.find((i) => i['@_key'] === 'Company');
                        const metadataApplication = metadatas.find(
                            (i) => i['@_key'] === 'Application'
                        );
                        if (
                            !metadataCompany ||
                            !metadataApplication ||
                            metadataCompany['@_value'] !== 'Creality' ||
                            !['Creality_Print', 'MakeNow'].includes(metadataApplication['@_value'])
                        ) {
                            onError(eIncompatible);
                            return;
                        }
                    }

                    {
                        // 2. 解析 Metadata/project_settings.config，得到 filament_colour 和 printer_model
                        const path = 'Metadata/project_settings.config';
                        const file = zip[path];
                        const jsonStr = textDecoder.decode(file);
                        const obj = JSON.parse(jsonStr);
                        filament_colour = obj.filament_colour;
                        printer_model = obj.printer_model;
                        if (
                            !Array.isArray(filament_colour) ||
                            filament_colour.lenght === 0 ||
                            !printer_model
                        ) {
                            onError(eMissingData);
                            return;
                        }
                    }

                    {
                        // 3. 解析 3D/_rels/3dmodel.model.rel
                        const path = '3D/_rels/3dmodel.model.rels';
                        const file = zip[path];
                        const xmlStr = textDecoder.decode(file);
                        const obj = xmlParser.parse(xmlStr);
                        const Relationships = obj.Relationships.Relationship;

                        // 4. 解析所有 3D/Objects/*.model，得到 modelInfosAll
                        // unit, id, position, vertexIndices, paintColors => modelInfo
                        Relationships.forEach((item) => {
                            const path = item['@_Target'].slice(1); // *.model 文件的路径
                            const file = zip[path];
                            const xmlStr = textDecoder.decode(file);
                            const obj = xmlParser.parse(xmlStr);

                            const unit = obj.model['@_unit'];
                            const object = obj.model.resources.object;
                            const id = object['@_id'];

                            // 从 object 中解析得到 position
                            const position = []; // 顶点数据
                            const vertexIndices = []; // 三角片的顶点的 index
                            const paintColors = [];

                            // 全部转为 millimeter 来处理
                            const mmConversionFactor = {
                                millimeter: 1,
                                centimeter: 10,
                                meter: 100,
                                inch: 25.4
                            }[unit];

                            object.mesh.vertices.vertex.forEach((item) => {
                                position.push(item['@_x'] * mmConversionFactor);
                                position.push(item['@_y'] * mmConversionFactor);
                                position.push(item['@_z'] * mmConversionFactor);
                            });

                            // object.mesh.vertices.vertex.forEach((item) => {
                            //     position.push(item['@_x'] * mmConversionFactor);
                            //     -position.push(item['@_z'] * mmConversionFactor);
                            //     position.push(item['@_y'] * mmConversionFactor);
                            // });

                            object.mesh.triangles.triangle.forEach((item) => {
                                vertexIndices.push(item['@_v1']);
                                vertexIndices.push(item['@_v2']);
                                vertexIndices.push(item['@_v3']);
                                paintColors.push(String(item['@_paint_color'])); // todo: paint_color="" 是否需要额外处理
                            });

                            const modelInfo = {
                                unit,
                                id,
                                position,
                                vertexIndices,
                                paintColors,
                                object_id: null,
                                extruder: null,
                                name: null,
                                modelSettingsTransform: null,
                                modelTransform: null,
                                geometryGroups: []
                            };
                            modelInfosAll.push(modelInfo);
                        });
                    }

                    {
                        // 5. 解析 Metadata/model_settings.config
                        const path = 'Metadata/model_settings.config';
                        const file = zip[path];
                        const xmlStr = textDecoder.decode(file);
                        const obj = xmlParser.parse(xmlStr);

                        // todo: part 可能是数组吗？
                        // object_id, extruder, name => modelInfo
                        obj.config.object.forEach((item) => {
                            const object_id = item['@_id'];
                            const extruder = item.metadata.find(
                                (item) => item['@_key'] === 'extruder'
                            )['@_value'];
                            const name = item.metadata.find((item) => item['@_key'] === 'name')[
                                '@_value'
                            ];

                            const id = item.part['@_id']; // 对应 modelInfo.id

                            const modelInfo = modelInfosAll.find((item) => item.id === id);
                            modelInfo.object_id = object_id;
                            modelInfo.extruder = extruder;
                            modelInfo.name = name;
                        });

                        // modelSettingsTransform => modelInfo
                        obj.config.assemble.assemble_item.forEach((item) => {
                            const transform = item['@_transform'];
                            const object_id = item['@_object_id']; // 对应 modelInfo.object_id
                            const modelInfo = modelInfosAll.find(
                                (item) => item.object_id === object_id
                            );
                            modelInfo.modelSettingsTransform = transform;
                        });

                        // 6. 解析 3D/3dmodel.model
                        // modelTransform => modelInfo
                        {
                            const path = '3D/3dmodel.model';
                            const file = zip[path];
                            const xmlStr = textDecoder.decode(file);
                            const obj = xmlParser.parse(xmlStr);
                            obj.model.build.item.forEach((item) => {
                                const objectid = item['@_objectid'];
                                const transform = item['@_transform'];
                                const modelInfo = modelInfosAll.find(
                                    (item) => item.object_id === objectid
                                );
                                modelInfo.modelTransform = transform;
                            });
                        }

                        // 最终组装得到 plateInfos
                        obj.config.plate.forEach((item) => {
                            const plater_id = item.metadata.find(
                                (item) => item['@_key'] === 'plater_id'
                            )['@_value'];
                            const plater_name = item.metadata.find(
                                (item) => item['@_key'] === 'plater_name'
                            )['@_value'];

                            const modelInfos = [];
                            item.model_instance.forEach((item) => {
                                const object_id = item.metadata.find(
                                    (item) => item['@_key'] === 'object_id'
                                )['@_value'];

                                const modelInfo = modelInfosAll.find(
                                    (item) => item.object_id === object_id
                                );
                                modelInfos.push(modelInfo);
                            });

                            const plateInfo = {
                                plater_id,
                                plater_name,
                                modelInfos
                            };
                            plateInfos.push(plateInfo);
                        });
                    }

                    // paintColors + extruder 计算得到 geometryGroups
                    plateInfos.forEach((item) => {
                        item.modelInfos.forEach((modelInfo) => {
                            const { paintColors, extruder } = modelInfo;
                            modelInfo.geometryGroups = generateGeometryGroups(
                                paintColors,
                                extruder
                            );
                        });
                    });

                    // 写入到 threejs Object3D 中
                    // 注意：索引 BufferGeometry + 顶点属性 + flat 输出，是无法实现每个面独立颜色的
                    // 这里使用 geometry.groups 来实现不同面片使用不同颜色
                    const groupModels = [];
                    plateInfos.forEach((plateInfo) => {
                        const models = [];
                        plateInfo.modelInfos.forEach((modelInfo) => {
                            const {
                                position,
                                vertexIndices,
                                geometryGroups,
                                modelSettingsTransform,
                                modelTransform
                            } = modelInfo;
                            const geometry = new THREE.BufferGeometry();
                            geometryGroups.forEach((item) => {
                                const { start, count, materialIndex } = item;
                                geometry.addGroup(start, count, materialIndex);
                            });
                            geometry.setAttribute(
                                'position',
                                new THREE.Float32BufferAttribute(position, 3)
                            );
                            geometry.setIndex(
                                new THREE.BufferAttribute(new Uint32Array(vertexIndices), 1)
                            );
                            geometry.computeVertexNormals();

                            const materials = [];
                            filament_colour.forEach((color) => {
                                // 塑料质感
                                const material = new THREE.MeshStandardMaterial({
                                    color,
                                    side: 2,
                                    roughness: 0.65, // 表面粗糙度（0=镜面，1=哑光）
                                    metalness: 0.0, // 塑料不属于金属，设为0
                                    envMapIntensity: 0.5 // 环境反射强度
                                });
                                materials.push(material);
                            });

                            // apply transform
                            // 两种情况：两个 transform 相同 or 不同
                            // 相同：则 apply 一个即可；不同：需要 apply both
                            let finalMatrix = null;
                            finalMatrix = transformToMatrix4(modelTransform);

                            console.log('modelTransform: ' + modelTransform);
                            console.log('modelSettingsTransform: ' + modelSettingsTransform);
                            // if (modelTransform === modelSettingsTransform) {
                            //     finalMatrix = transformToMatrix4(modelTransform);
                            // } else {
                            //     const modelMatrix = transformToMatrix4(modelTransform);
                            //     const settingsMatrix = transformToMatrix4(modelSettingsTransform);
                            //     // 计算最终变换矩阵
                            //     finalMatrix = new THREE.Matrix4();
                            //     finalMatrix.multiplyMatrices(settingsMatrix, modelMatrix);
                            // }
                            const model = new THREE.Mesh(geometry, materials);

                            // 1. 应用 3MF 自带矩阵
                            model.matrix.copy(finalMatrix);

                            // 2. 从矩阵中提取 position / rotation / scale
                            model.matrix.decompose(model.position, model.quaternion, model.scale);

                            // 3. 开启自动更新（以后可以自由修改）
                            model.matrixAutoUpdate = true;

                            models.push(model);
                        });
                        groupModels.push(models);
                    });

                    onLoad(groupModels);
                } catch (e) {
                    console.error(e);
                    scope.manager.itemError(url);
                    onError(e);
                }
            },
            onProgress,
            onError
        );
    }
}

export default index;
